<?php
/**
 * Configuration
 * PHP version 7.4
 *
 * @category Class
 * @package  {{invokerPackage}}
 * @author   Konfig
 * @link     https://konfigthis.com
 */

{{>partial_header}}

namespace {{invokerPackage}};

// This value is used to help us determine if the user has passed in a value for
// a particular argument
define('SENTINEL_VALUE', '__SENTINEL_VALUE__');

{{#hasOAuthMethods}}
use GuzzleHttp\Client;

{{/hasOAuthMethods}}
class Configuration
{
    public const BOOLEAN_FORMAT_INT = 'int';
    public const BOOLEAN_FORMAT_STRING = 'string';

    /**
     * @var Configuration
     */
    private static $defaultConfiguration;

    /**
     * Associate array to store API key(s)
     *
     * @var string[]
     */
    protected $apiKeys = [];

    /**
     * Associate array to store API prefix (e.g. Bearer)
     *
     * @var string[]
     */
    protected $apiKeyPrefixes = [];

    /**
     * Access token for OAuth/Bearer authentication
     *
     * @var string
     */
    protected $accessToken = '';
{{#hasOAuthMethods}}
{{#authMethods}}
{{#-first}}

    /**
     * Token url for OAuth/Application authentication
     *
     * @var string
     */
    protected {{^supportPhp7}}string {{/supportPhp7}}$tokenUrl = "{{tokenUrl}}";

    /**
     * Access token for OAuth/Application authentication
     *
     * @var string
     */
    protected {{^supportPhp7}}string {{/supportPhp7}}$clientId = '';

    /**
     * Access token for OAuth/Application authentication
     *
     * @var string
     */
    protected {{^supportPhp7}}string {{/supportPhp7}}$clientSecret = '';
{{/-first}}
{{/authMethods}}
{{/hasOAuthMethods}}

    /**
     * Boolean format for query string
     *
     * @var string
     */
    protected $booleanFormatForQueryString = self::BOOLEAN_FORMAT_STRING;

    /**
     * Username for HTTP basic authentication
     *
     * @var string
     */
    protected $username = '';

    /**
     * Password for HTTP basic authentication
     *
     * @var string
     */
    protected $password = '';

    /**
     * Set to false if you want to skip SSL verification in HTTP request
     *
     * @var bool
     */
    protected $verifySsl = true;

    /**
     * The host
     *
     * @var string
     */
    protected $host = '{{basePath}}';

    /**
     * User agent of the HTTP request, set to "Konfig/{version}/PHP" by default
     *
     * @var string
     */
    protected $userAgent = '{{{httpUserAgent}}}{{^httpUserAgent}}Konfig/{{{artifactVersion}}}{{^artifactVersion}}1.0.0{{/artifactVersion}}/PHP{{/httpUserAgent}}';

    /**
     * Debug switch (default set to false)
     *
     * @var bool
     */
    protected $debug = false;

    /**
     * Debug file location (log to STDOUT by default)
     *
     * @var string
     */
    protected $debugFile = 'php://output';

    /**
     * Debug file location (log to STDOUT by default)
     *
     * @var string
     */
    protected $tempFolderPath;
    {{#clientStateSetterGetterCamelCase}}

    /**
     * {{state}} - client state from konfig.yaml
     */
    protected {{^supportPhp7}}?string {{/supportPhp7}}${{state}} = '';


    /**
     * {{state}} - client state setter from konfig.yaml
     */
    public function {{setter}}(${{state}})
    {
        $this->{{state}} = ${{state}};
        return $this;
    }

    /**
     * {{state}} - client state getter from konfig.yaml
     */
    public function {{getter}}()
    {
        return $this->{{state}};
    }
    {{/clientStateSetterGetterCamelCase}}
    {{#clientStateWithExamplesSetterGetterCamelCase}}

    /**
     * {{state}} - client state from konfig.yaml
     */
    protected {{^supportPhp7}}?string {{/supportPhp7}}${{state}} = '';


    /**
     * {{state}} - client state setter from konfig.yaml
     */
    public function {{setter}}(${{state}})
    {
        $this->{{state}} = ${{state}};
        return $this;
    }

    /**
     * {{state}} - client state getter from konfig.yaml
     */
    public function {{getter}}()
    {
        return $this->{{state}};
    }
    {{/clientStateWithExamplesSetterGetterCamelCase}}

    /**
     * Constructor
     */
    public function __construct(
        {{#clientState}}
        string ${{.}} = null,
        {{/clientState}}
        {{#clientStateWithExamples}}
        string ${{name}} = null,
        {{/clientStateWithExamples}}
        {{#apiKeyMethods}}
        string ${{> api_key_name}} = null,
        {{/apiKeyMethods}}
        string $host = '{{basePath}}'
    )
    {
        $this->host = $host;
        $this->tempFolderPath = sys_get_temp_dir();
        {{#clientStateSetterGetterCamelCase}}
        $this->{{setter}}(${{state}});
        {{/clientStateSetterGetterCamelCase}}
        {{#clientStateWithExamplesSetterGetterCamelCase}}
        $this->{{setter}}(${{state}});
        {{/clientStateWithExamplesSetterGetterCamelCase}}
        {{#apiKeyMethods}}
        $this->setApiKey("{{> api_key_name}}", ${{> api_key_name}});
        {{#vendorExtensions.x-konfig-prefix}}
        $this->setApiKeyPrefix("{{> api_key_name}}", "{{vendorExtensions.x-konfig-prefix}}");
        {{/vendorExtensions.x-konfig-prefix}}
        {{/apiKeyMethods}}
    }

    /**
     * Sets API key
     *
     * @param string $apiKeyIdentifier API key identifier (authentication scheme)
     * @param string $key              API key or token
     *
     * @return $this
     */
    public function setApiKey($apiKeyIdentifier, $key)
    {
        $this->apiKeys[$apiKeyIdentifier] = $key;
        return $this;
    }

    /**
     * Gets API key
     *
     * @param string $apiKeyIdentifier API key identifier (authentication scheme)
     *
     * @return null|string API key or token
     */
    public function getApiKey($apiKeyIdentifier)
    {
        return isset($this->apiKeys[$apiKeyIdentifier]) ? $this->apiKeys[$apiKeyIdentifier] : null;
    }

    /**
     * Sets the prefix for API key (e.g. Bearer)
     *
     * @param string $apiKeyIdentifier API key identifier (authentication scheme)
     * @param string $prefix           API key prefix, e.g. Bearer
     *
     * @return $this
     */
    public function setApiKeyPrefix($apiKeyIdentifier, $prefix)
    {
        $this->apiKeyPrefixes[$apiKeyIdentifier] = $prefix;
        return $this;
    }

    /**
     * Gets API key prefix
     *
     * @param string $apiKeyIdentifier API key identifier (authentication scheme)
     *
     * @return null|string
     */
    public function getApiKeyPrefix($apiKeyIdentifier)
    {
        return isset($this->apiKeyPrefixes[$apiKeyIdentifier]) ? $this->apiKeyPrefixes[$apiKeyIdentifier] : null;
    }

    /**
     * Sets the access token for OAuth
     *
     * @param string $accessToken Token for OAuth
     *
     * @return $this
     */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;
        return $this;
    }

    /**
     * Gets the access token for OAuth
     *
     * @return string Access token for OAuth
     */
    public function getAccessToken()
    {
        return $this->accessToken;
    }

    /**
     * Sets boolean format for query string.
     *
     * @param string $booleanFormatForQueryString Boolean format for query string
     *
     * @return $this
     */
    public function setBooleanFormatForQueryString(string $booleanFormat)
    {
        $this->booleanFormatForQueryString = $booleanFormat;

        return $this;
    }

    /**
     * Gets boolean format for query string.
     *
     * @return string Boolean format for query string
     */
    public function getBooleanFormatForQueryString(): string
    {
        return $this->booleanFormatForQueryString;
    }

    /**
     * Sets the username for HTTP basic authentication
     *
     * @param string $username Username for HTTP basic authentication
     *
     * @return $this
     */
    public function setUsername($username)
    {
        $this->username = $username;
        return $this;
    }

    /**
     * Gets the username for HTTP basic authentication
     *
     * @return string Username for HTTP basic authentication
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Sets the password for HTTP basic authentication
     *
     * @param string $password Password for HTTP basic authentication
     *
     * @return $this
     */
    public function setPassword($password)
    {
        $this->password = $password;
        return $this;
    }

    /**
     * Gets the password for HTTP basic authentication
     *
     * @return string Password for HTTP basic authentication
     */
    public function getPassword()
    {
        return $this->password;
    }

{{#hasOAuthMethods}}
    /**
     * Sets the tokenUrl for OAuth
     *
     * @param string $tokenUrl
     *
     * @return $this
     */
    public function setTokenUrl($tokenUrl)
    {
        $this->tokenUrl = $tokenUrl;
        return $this;
    }

    /**
     * Gets the tokenUrl for OAuth
     *
     * @return string $tokenUrl
     */
    public function getTokenUrl()
    {
        return $this->tokenUrl;
    }

{{/hasOAuthMethods}}
    /**
     * Set verifySsl
     *
     * @param bool $verifySsl
     *
     * @return $this
     */
    public function setVerifySsl($verifySsl)
    {
        $this->verifySsl = $verifySsl;
        return $this;
    }

    /**
     * Gets the verifySsl
     *
     * @return bool verifySsl
     */
    public function getVerifySsl()
    {
        return $this->verifySsl;
    }

    /**
     * Sets the host
     *
     * @param string $host Host
     *
     * @return $this
     */
    public function setHost($host)
    {
        $this->host = $host;
        return $this;
    }

    /**
     * Gets the host
     *
     * @return string Host
     */
    public function getHost()
    {
        return $this->host;
    }

    /**
     * Sets the user agent of the api client
     *
     * @param string $userAgent the user agent of the api client
     *
     * @throws \InvalidArgumentException
     * @return $this
     */
    public function setUserAgent($userAgent)
    {
        if (!is_string($userAgent)) {
            throw new \InvalidArgumentException('User-agent must be a string.');
        }

        $this->userAgent = $userAgent;
        return $this;
    }

    /**
     * Gets the user agent of the api client
     *
     * @return string user agent
     */
    public function getUserAgent()
    {
        return $this->userAgent;
    }

    /**
     * Sets debug flag
     *
     * @param bool $debug Debug flag
     *
     * @return $this
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
        return $this;
    }

    /**
     * Gets the debug flag
     *
     * @return bool
     */
    public function getDebug()
    {
        return $this->debug;
    }

    /**
     * Sets the debug file
     *
     * @param string $debugFile Debug file
     *
     * @return $this
     */
    public function setDebugFile($debugFile)
    {
        $this->debugFile = $debugFile;
        return $this;
    }

    /**
     * Gets the debug file
     *
     * @return string
     */
    public function getDebugFile()
    {
        return $this->debugFile;
    }

    /**
     * Sets the temp folder path
     *
     * @param string $tempFolderPath Temp folder path
     *
     * @return $this
     */
    public function setTempFolderPath($tempFolderPath)
    {
        $this->tempFolderPath = $tempFolderPath;
        return $this;
    }

    /**
     * Gets the temp folder path
     *
     * @return string Temp folder path
     */
    public function getTempFolderPath()
    {
        return $this->tempFolderPath;
    }

    /**
     * Gets the default configuration instance
     *
     * @return Configuration
     */
    public static function getDefaultConfiguration()
    {
        if (self::$defaultConfiguration === null) {
            self::$defaultConfiguration = new Configuration();
        }

        return self::$defaultConfiguration;
    }

    /**
     * Sets the default configuration instance
     *
     * @param Configuration $config An instance of the Configuration Object
     *
     * @return void
     */
    public static function setDefaultConfiguration(Configuration $config)
    {
        self::$defaultConfiguration = $config;
    }

    /**
     * Gets the essential information for debugging
     *
     * @return string The report for debugging
     */
    public static function toDebugReport()
    {
        $report  = 'PHP SDK ({{invokerPackage}}) Debug Report:' . PHP_EOL;
        $report .= '    OS: ' . php_uname() . PHP_EOL;
        $report .= '    PHP Version: ' . PHP_VERSION . PHP_EOL;
        $report .= '    The version of the OpenAPI document: {{version}}' . PHP_EOL;
	    {{#artifactVersion}}
        $report .= '    SDK Package Version: {{.}}' . PHP_EOL;
    	{{/artifactVersion}}
        $report .= '    Temp Folder Path: ' . self::getDefaultConfiguration()->getTempFolderPath() . PHP_EOL;

        return $report;
    }

    /**
     * Get API key (with prefix if set)
     *
     * @param  string $apiKeyIdentifier name of apikey
     *
     * @return null|string API key with the prefix
     */
    public function getApiKeyWithPrefix($apiKeyIdentifier)
    {
        $prefix = $this->getApiKeyPrefix($apiKeyIdentifier);
        $apiKey = $this->getApiKey($apiKeyIdentifier);

        if ($apiKey === null) {
            return null;
        }

        if ($prefix === null) {
            $keyWithPrefix = $apiKey;
        } else {
            $keyWithPrefix = $prefix . $apiKey;
        }

        return $keyWithPrefix;
    }

    /**
     * Returns an array of host settings
     *
     * @return array an array of host settings
     */
    public function getHostSettings()
    {
        return [
        {{#servers}}
            [
                "url" => "{{{url}}}",
                "description" => "{{{description}}}{{^description}}No description provided{{/description}}",
                {{#variables}}
                {{#-first}}
                "variables" => [
                {{/-first}}
                    "{{{name}}}" => [
                        "description" => "{{{description}}}{{^description}}No description provided{{/description}}",
                        "default_value" => "{{{defaultValue}}}",
                        {{#enumValues}}
                        {{#-first}}
                        "enum_values" => [
                        {{/-first}}
                            "{{{.}}}"{{^-last}},{{/-last}}
                        {{#-last}}
                        ]
                        {{/-last}}
                        {{/enumValues}}
                    ]{{^-last}},{{/-last}}
                {{#-last}}
                ]
                {{/-last}}
                {{/variables}}
            ]{{^-last}},{{/-last}}
        {{/servers}}
        ];
    }

    /**
     * Returns URL based on host settings, index and variables
     *
     * @param array      $hostSettings array of host settings, generated from getHostSettings() or equivalent from the API clients
     * @param int        $hostIndex    index of the host settings
     * @param array|null $variables    hash of variable and the corresponding value (optional)
     * @return string URL based on host settings
     */
    public static function getHostString(array $hostsSettings, $hostIndex, array $variables = null)
    {
        if (null === $variables) {
            $variables = [];
        }

        // check array index out of bound
        if ($hostIndex < 0 || $hostIndex >= count($hostsSettings)) {
            throw new \InvalidArgumentException("Invalid index $hostIndex when selecting the host. Must be less than " . count($hostsSettings));
        }

        $host = $hostsSettings[$hostIndex];
        $url = $host["url"];

        // go through variable and assign a value
        foreach ($host["variables"] ?? [] as $name => $variable) {
            if (array_key_exists($name, $variables)) { // check to see if it's in the variables provided by the user
                if (!isset($variable['enum_values']) || in_array($variables[$name], $variable["enum_values"], true)) { // check to see if the value is in the enum
                    $url = str_replace("{" . $name . "}", $variables[$name], $url);
                } else {
                    throw new \InvalidArgumentException("The variable `$name` in the host URL has invalid value " . $variables[$name] . ". Must be " . join(',', $variable["enum_values"]) . ".");
                }
            } else {
                // use default value
                $url = str_replace("{" . $name . "}", $variable["default_value"], $url);
            }
        }

        return $url;
    }

    /**
     * Returns URL based on the index and variables
     *
     * @param int        $index     index of the host settings
     * @param array|null $variables hash of variable and the corresponding value (optional)
     * @return string URL based on host settings
     */
    public function getHostFromSettings($index, $variables = null)
    {
        return self::getHostString($this->getHostSettings(), $index, $variables);
    }
{{#hasOAuthMethods}}

    public function setClientId($client_id)
    {
        $this->clientId = $client_id;
        return $this;
    }

    public function setClientSecret($client_secret)
    {
        $this->clientSecret = $client_secret;
        return $this;
    }

    public function refreshOAuthAccessToken()
    {
        if (empty($this->clientId) || empty($this->clientSecret)) return;
        $client = new Client();

        $url = $this->tokenUrl;

        $params = [
            'form_params' => [
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
                'grant_type' => 'client_credentials'
            ]
        ];

        $response = $client->request('POST', $url, $params);

        $data = json_decode($response->getBody(), true);
        $this->accessToken = $data['access_token'];
    }
{{/hasOAuthMethods}}
}
